<?php
class Db {
	private $_aMasterConn = array ();
	private $_aSlaveConn = array ();
	private $_rResult;
	private $_aField = array ();
	private $_aTable = array ();
	private $_aServerInfo = array ();
	
	public function __construct() {
		$this->parseConf ();
	}
	
	public function connect($aInfo, $sServer = 'db', $bMaster = true) {
		try {
			$sDsn = $aInfo ['type'] ? $aInfo ['type'] : 'mysql';
			if ($aInfo ['type'] == 'sqlite') {
				$sDsn .= ':' . $aInfo ['name'];
			} elseif ($aInfo ['type'] == 'oracle') {
				$sDsn = 'oci:dbname=//' . $aInfo ['host'] . '/' . $aInfo ['name'];
				if ($aInfo ['charset']) {
					$sDsn .= ';charset=' . $aInfo ['charset'];
				} else {
					$sDsn .= ';charset=ZHS16GBK';
				}
			} elseif ($aInfo ['type'] == 'sqlite_memory') {
				$sDsn = 'sqlite::memroy:';
			} else {
				$sDsn .= ':host=' . $aInfo ['host'] . ';dbname=' . $aInfo ['name'];
			}
			$oDbh = new PDO ( $sDsn, $aInfo ['user'], $aInfo ['password'] );
			$oDbh->setAttribute ( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
			if ($aInfo ['type'] == 'mysql' || $aInfo ['type'] == null) {
				$oDbh->setAttribute ( PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true );
				$sCharset = $aInfo ['charset'] ? $aInfo ['charset'] : 'utf8';
				$oDbh->exec ( 'SET NAMES ' . $sCharset );
			}
			$this->_aServerInfo [$sServer] = $aInfo;
			$this->_aField [$sServer] = array ();
			if ($bMaster) {
				$this->_aMasterConn [$sServer] = $oDbh;
			} else {
				$this->_aSlaveConn [$sServer] [] = $oDbh;
			}
			return $oDbh;
		} catch ( Exception $e ) {
			$ee = new ExceptionExt ( $e->getMessage () );
			$ee->showError ();
		}
	}
	
	public function parseConf($sServer = 'db') {
		list ( $sServer, $sType ) = explode ( '_', $sServer );
		$aInfo = Base::getConf ( $sServer );
		if (is_array ( $aInfo )) {
			$aMatch = array ();
			$aDbInfo = array ();
			if ($sServer == 'db') {
				$sPreg = '/^([a-z]+)_{0,1}(\d*)$/i';
			} else {
				if ($sType == 'cache') {
					$sPreg = '/^cache_([a-z]+)_{0,1}(\d*)$/i';
				} else {
					$sPreg = '/^db_([a-z]+)_{0,1}(\d*)$/i';
				}
			}
			foreach ( $aInfo as $sKey => $sRow ) {
				preg_match ( $sPreg, $sKey, $aMatch );
				if (count ( $aMatch )) {
					$aDbInfo [intval ( $aMatch [2] )] [$aMatch [1]] = $sRow;
				}
			}
			if (! empty ( $aDbInfo )) {
				sort ( $aDbInfo );
				$i = 0;
				foreach ( $aDbInfo as $aRow ) {
					if ($i == 0) {
						$this->connect ( $aRow, $sServer );
					} else {
						$this->connect ( $aRow, $sServer, false );
					}
				}
				return true;
			} else {
				return false;
			}
		}
		return false;
	}
	
	public function begin($sServer = 'db') {
		$oServer = $this->_selectServer ( $sServer );
		$oServer->beginTransaction ();
	}
	
	public function commit($sServer = 'db') {
		$oServer = $this->_selectServer ( $sServer );
		$oServer->commit ();
	}
	
	public function back($sServer = 'db') {
		$oServer = $this->_selectServer ( $sServer );
		$oServer->rollBack ();
	}
	
	public function exec($sSql, $aValue = null, $sServer = 'db') {
		try {
			if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				$sSql = str_replace ( '`', '', $sSql );
			}
			$oServer = $this->_selectServer ( $sServer );
			$rResult = $oServer->prepare ( $sSql );
			if (is_array ( $aValue )) {
				$i = 1;
				foreach ( $aValue as $sKey => $val ) {
					if (is_int ( $sKey )) {
						$rResult->bindParam ( $i ++, $aValue [$sKey] );
					} else {
						$rResult->bindParam ( ':' . $sKey, $aValue [$sKey] );
					}
				}
			} elseif ($aValue !== null) {
				$rResult->bindParam ( 1, $aValue );
			}
			$rResult->execute ();
			return $rResult->rowCount ();
		} catch ( Exception $e ) {
			$e->getMessage ();
		}
		return false;
	}
	
	public function query($sSql, $aValue = null, $sServer = 'db') {
		$this->_rResult = null;
		
		try {
			$oServer = $this->_selectServer ( $sServer, false );
			if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				$sSql = str_replace ( '`', '', $sSql );
			}
			$rResult = $oServer->prepare ( $sSql );
			
			if (is_array ( $aValue )) {
				$i = 1;
				foreach ( $aValue as $sKey => $val ) {
					if (is_int ( $sKey )) {
						$rResult->bindParam ( $i ++, $aValue [$sKey] );
					} else {
						$rResult->bindParam ( ':' . $sKey, $aValue [$sKey] );
					}
				}
			} elseif ($aValue !== null) {
				$rResult->bindParam ( 1, $aValue );
			}
			
			$rResult->execute ();
			$this->_rResult = $rResult;
			return $rResult;
		} catch ( Exception $e ) {
			echo $sSql;
			echo $e->getMessage ();
		}
		return false;
	}
	
	public function getAll($sSql, $aValue = null, $sServer = 'db') {
		$this->query ( $sSql, $aValue, $sServer );
		return $this->getRows ();
	}
	
	public function getInsertId($sServer = 'db') {
		$oServer = $this->_selectServer ( $sServer );
		return $oServer->lastInsertId ();
	}
	
	private function _selectServer($sServer, $bMaster = true, $bReload = false) {
		$iCount = count ( $this->_aSlaveConn [$sServer] );
		if ($iCount > 0 && $bMaster != true) {
			$i = floor ( rand ( 0, $iCount ) );
			return $this->_aSlaveConn [$sServer] [$i];
		} else {
			if (! $this->_aMasterConn [$sServer] && $bReload == false) {
				$this->parseConf ( $sServer );
				$this->_selectServer ( $sServer, $bMaster, true );
				return true;
			} else {
				if (! $this->_aMasterConn [$sServer]) {
					return false;
				}
				return $this->_aMasterConn [$sServer];
			}
		}
	}
	
	public function getRow($rResult = null, $sModel = 'assoc') {
		if ($rResult === null) {
			$rResult = $this->_rResult;
		}
		
		if ($rResult === null) {
			return false;
		}
		
		try {
			switch (strtolower ( $sModel )) {
				case 'assoc' :
					$sModel = PDO::FETCH_ASSOC;
					break;
				case 'num' :
					$sModel = PDO::FETCH_NUM;
					break;
				default :
					$sModel = PDO::FETCH_BOTH;
			}
			return $rResult->fetch ( $sModel );
		} catch ( Exception $e ) {
			$e->getMessage ();
		}
		
		return false;
	}
	
	public function getRows($rResult = null, $sModel = 'assoc') {
		if ($rResult === null) {
			$rResult = $this->_rResult;
		}
		
		if ($rResult === null) {
			return false;
		}
		
		try {
			switch (strtolower ( $sModel )) {
				case 'assoc' :
					$sModel = PDO::FETCH_ASSOC;
					break;
				case 'num' :
					$sModel = PDO::FETCH_NUM;
					break;
				default :
					$sModel = PDO::FETCH_BOTH;
			}
			return $rResult->fetchAll ( $sModel );
		} catch ( Exception $e ) {
			$e->getMessage ();
		}
		return false;
	}
	
	public function getResult($iIndex = 0, $rResult = null) {
		if ($rResult === null) {
			$rResult = $this->_rResult;
		}
		
		if ($rResult === null) {
			return false;
		}
		
		return $rResult->fetchColumn ( $iIndex );
	}
	
	public function getOne($sSql, $aValue = null, $sServer = 'db') {
		$this->query ( $sSql, $aValue, $sServer );
		$aRow = $this->getRow ();
		if ($aRow) {
			if (count ( $aRow ) == 1) {
				return $aRow [key ( $aRow )];
			} else {
				return $aRow;
			}
		} else {
			return false;
		}
	}
	
	public function getList($sSql, $aValue = null, $iPage = 1, $iLimit = 0, $sServer = 'db') {
		$items = $this->_getItems ( $sSql, $aValue, $sServer );
		if ($iLimit == 0) {
			$iPages = 1;
		} else {
			$iPages = floor ( ($items - 1) / $iLimit ) + 1;
		}
		
		if ($iPage == - 1 || $iPage > $iPages) {
			$iPage = $iPages;
		}
		if (! $iPage) {
			$iPage = 1;
		}
		if ($iLimit > 0) {
			if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				if (preg_match ( '/.+group\sby.+/is', $sSql )) {
					list ( $sSql, $sGroup ) = explode ( '###split###', preg_replace ( '/(.+?)(\s+group\s+by.+$)/is', '$1###split###$2', $sSql ) );
				}
				if (preg_match ( '/.+order\sby.+/is', $sSql )) {
					list ( $sSql, $sOrder ) = explode ( '###split###', preg_replace ( '/(.+?)(\s+order\s+by.+$)/is', '$1###split###$2', $sSql ) );
				}
				if (preg_match ( '/.+where.+/is', $sSql )) {
					list ( $sSql, $sWhere ) = explode ( '###split###', preg_replace ( '/(.+?)(\s+where.+$)/is', '$1###split###$2', $sSql ) );
				}
				$sSubSql = preg_replace ( '/(\s+from.+$)/is', ', rownum r $1', $sSql );
				if ($sWhere) {
					$sSubSql .= $sWhere . ' AND rownum <= ?';
				} else {
					$sSubSql .= ' WHERE rownum <= ?';
				}
				$aValue [] = $iPage * $iLimit;
				$sSql = preg_replace ( '/select\s+(.+?)\s+from\s+(.+$)/is', 'SELECT * FROM $2', $sSql );
				$sSql = preg_replace ( '/from\s+(.+$)/is', 'FROM (' . $sSubSql . ')', $sSql );
				$sSql .= ' WHERE r > ?';
				$sSql .= $sOrder;
				$aValue [] = ($iPage - 1) * $iLimit;
			} else {
				$sSql .= ' LIMIT ' . ($iPage - 1) * $iLimit . ', ' . $iLimit;
			}
		}
		$this->query ( $sSql, $aValue, $sServer );
		return array ('items' => $items, 'pages' => $iPages, 'page' => $iPage, 'data' => $this->getRows () );
	}
	
	private function _getItems($sSql, $aValue, $sServer) {
		$sSql = preg_replace ( '/[\r\n]+/', ' ', $sSql );
		$sSql = preg_replace ( '/(select).*[\s\r\n]+(from.*)/is', '$1 COUNT(1) $2', $sSql );
		$iPos = strripos ( $sSql, 'ORDER BY' );
		if ($iPos !== false) {
			$sSql = substr ( $sSql, 0, $iPos );
		}
		$this->query ( $sSql, $aValue, $sServer );
		if (strripos ( $sSql, 'GROUP BY' )) {
			$rows = $this->getRows ();
			return count ( $rows );
		} else {
			return $this->getResult ();
		}
	}
	
	public function import($sSqlFile, $sServer = 'db') {
		$iVersion = $this->getVersion ( $sServer );
		$rFp = fopen ( $sSqlFile, 'r' );
		$sSql = '';
		while ( ! feof ( $rFp ) ) {
			$sLine = trim ( fgets ( $rFp, 8000 ) );
			$aMatch = array ();
			
			if ($sLine == '') {
				continue;
			}
			
			if (preg_match ( '/^\-\-/', $sLine )) {
				continue;
			}
			
			preg_match ( '/\/\*\!(\d+)\s*(.+)\s*\*\/;/', $sLine, $aMatch );
			if (count ( $aMatch ) > 1) {
				if ($aMatch [1] <= $iVersion) {
					$this->exec ( $aMatch [2], $sServer );
				}
				continue;
			}
			
			$iLen = mb_strlen ( $sLine, 'utf-8' );
			$sQuot = '';
			for($i = 0; $i < $iLen; $i ++) {
				$sChar = mb_substr ( $sLine, $i, 1, 'utf-8' );
				$sSql .= $sChar;
				if (($sChar == '\'' || $sChar == '"')) {
					if ($sChar != $sQuot) {
						$sQuot = $sChar;
					} else {
						$sQuot = '';
					}
				} elseif ($sQuot == '') {
					if ($sChar == ';') {
						$this->exec ( $sSql, $sServer );
						$sSql = '';
					} elseif ($sChar == '\\') {
						$i ++;
						$sChar = mb_substr ( $sLine, $i, 1, 'utf-8' );
						$sSql .= $sChar;
					}
				}
			}
		}
	}
	
	public function getVersion($sServer = 'db') {
		$sSql = 'SELECT VERSION()';
		$sVersion = $this->getOne ( $sSql, $sServer );
		$aMatch = array ();
		preg_match ( '/^(\d+)\.(\d+)\.(\d+).*$/', $sVersion, $aMatch );
		return sprintf ( '%d%02d%02d', $aMatch [1], $aMatch [2], $aMatch [3] );
	}
	
	public function backup($sTable = null, $sFile = null) {
		if ($sFile === null) {
			$rFp = tmpfile ();
		} elseif (is_string ( $sFile )) {
			$rFp = fopen ( $sFile, 'w' );
		} else {
			$rFp = &$sFile;
		}
		if ($sTable == null) {
			$sSql = 'SHOW TABLES';
			$rRes = $this->query ( $sSql );
			while ( ($aRow = $this->getRow ( $rRes, 'num' )) !== false ) {
				$this->backup ( $aRow [0], $rFp );
			}
		} elseif (is_array ( $sTable )) {
			foreach ( $sTable as $sRow ) {
				$this->backup ( $sRow, $sFile );
			}
		} elseif ($sTable) {
			$sSql = "SHOW CREATE TABLE $sTable";
			$this->query ( $sSql );
			$aRow = $this->getRow ( null, 'num' );
			fwrite ( $rFp, $aRow [1] . ";\n\n" );
			$sSql = "SELECT * FROM $sTable";
			Base::$oBase->Db_query ( $sSql );
			while ( ($aRow = Base::$oBase->Db_getRow ()) !== false ) {
				$aValue = array ();
				foreach ( $aRow as $sRow ) {
					if (is_null ( $sRow )) {
						$aValue [] = 'NULL';
					} elseif (is_string ( $sRow )) {
						$aValue [] = '"' . addslashes ( $sRow ) . '"';
					} else {
						$aValue [] = $sRow;
					}
				}
				fwrite ( $rFp, "INSERT INTO `$sTable` VALUES (" . implode ( ', ', $aValue ) . ");\n" );
			}
			fwrite ( $rFp, "\n" );
		}
		if ($sFile === null) {
			$sString = '';
			fseek ( $rFp, 0 );
			while ( ! feof ( $rFp ) ) {
				$sString .= fread ( $rFp, 8000 );
			}
			return $sString;
		}
	}
	
	public function getField($sName, $sServer = 'db') {
		$sTable = strtolower ( $sName );
		if (! array_key_exists ( $sTable, $this->_aField [$sServer] )) {
			if ($this->_aServerInfo [$sServer] ['type'] == 'sqlite' || $this->_aServerInfo [$sServer] ['type'] == 'sqlite_memory') {
				$sSql = 'SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ?';
				$sSql = $this->getOne ( $sSql, array ('table', $sName ), $sServer );
				preg_match_all ( '/"(.+?)"/', $sSql, $aMatch );
				array_shift ( $aMatch [1] );
				$aField = array_unique ( $aMatch [1] );
			} elseif ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				$sSql = 'SELECT column_name FROM user_tab_columns WHERE table_name = upper(?)';
				$this->query ( $sSql, array ($sName ), $sServer );
				while ( ($aRow = $this->getRow ()) !== false ) {
					$aField [] = $aRow ['COLUMN_NAME'];
				}
			} else {
				$sSql = 'DESC `' . $sTable . '`';
				$this->query ( $sSql, null, $sServer );
				$aField = array ();
				while ( ($aRow = $this->getRow ()) !== false ) {
					$aField [] = $aRow ['Field'];
				}
				$this->_aField [$sServer] [$sTable] = $aField;
			}
			return $aField;
		} else {
			return $this->_aField [$sServer] [$sTable];
		}
	}
	
	public function getPre($sTable, $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$aFieldPre = explode ( '_', $aField [0] );
		if (count ( $aFieldPre ) == 1) {
			$sFieldPre = '';
		} else {
			$sFieldPre = $aFieldPre [0] . '_';
		}
		return $sFieldPre;
	}
	
	public function insert($sTable, $aData, $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		$i = 0;
		foreach ( $aData as $sKey => $sRow ) {
			if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				$sKey = strtoupper ( $sKey );
			}
			if (in_array ( $sFieldPre . $sKey, $aField )) {
				if ($i == 0) {
					$sField = '`' . $sFieldPre . $sKey . '`';
					$sValue = '?';
				} else {
					$sField .= ', `' . $sFieldPre . $sKey . '`';
					$sValue .= ', ?';
				}
				$aValue [] = $sRow;
				$i ++;
			}
		}
		$sSql = 'INSERT INTO `' . $sTable . '` (' . $sField . ') VALUES (' . $sValue . ')';
		if ($this->exec ( $sSql, $aValue, $sServer )) {
			if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				return true;
			} else {
				return $this->getInsertId ( $sServer );
			}
		} else {
			return false;
		}
	}
	
	public function replace($sTable, $aData, $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		$aValue = array ();
		foreach ( $aData as $sKey => $sRow ) {
			if (in_array ( $sFieldPre . $sKey, $aField )) {
				$aSet [] = $sFieldPre . $sKey . ' = ?';
				$aValue [] = $sRow;
			}
		}
		$sSql = 'REPLACE `' . $sTable . '` SET ' . implode ( ', ', $aSet );
		$this->exec ( $sSql, $aValue );
		return $this->getInsertId ();
	}
	
	public function update($sTable, $aData, $aPRI = array(), $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		foreach ( $aData as $sKey => $sRow ) {
			if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
				$sKey = strtoupper ( $sKey );
			}
			if (in_array ( $sFieldPre . $sKey, $aField )) {
				$aSet [] = '`' . $sFieldPre . $sKey . '` = ?';
				$aValue [] = $sRow;
			}
		}
		if (empty ( $aPRI )) {
			$aWhere [] = '`' . $sFieldPre . 'id` = ?';
			$aValue [] = $aData ['id'];
		} else {
			while ( ($aArr = each ( $aPRI )) !== false ) {
				$aValue [] = $aArr [1];
				$aWhere [] = '`' . $sFieldPre . $aArr [0] . '` = ?';
			}
		}
		$sSql = 'UPDATE `' . $sTable . '` SET ' . implode ( ', ', $aSet ) . ' WHERE ' . implode ( ' AND ', $aWhere );
		return $this->exec ( $sSql, $aValue, $sServer );
	}
	
	public function getCount($sTable, $iId, $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		if (is_array ( $iId )) {
			foreach ( $iId as $sKey => $sRow ) {
				if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
					$sKey = strtoupper ( $sKey );
				}
				if (in_array ( $sFieldPre . $sKey, $aField )) {
					if (is_array ( $sRow )) {
						$aWhere [] = $sFieldPre . $sKey . ' IN (?' . str_repeat ( ',?', count ( $sRow ) - 1 ) . ')';
						$aVaule = array_merge ( $aVaule, $sRow );
					} elseif (preg_match ( '/^(<>|>|<)(.+?)$/', $sRow, $aMatch )) {
						$aWhere [] = $sFieldPre . $sKey . $aMatch [1] . ' ?';
						$aVaule [] = trim ( $aMatch [2] );
					} else {
						$aWhere [] = $sFieldPre . $sKey . ' = ?';
						$aVaule [] = $sRow;
					}
				}
			}
		} elseif ($iId) {
			$aWhere [] = $sFieldPre . 'id = ?';
			$aVaule = $iId;
		} else {
			$aWhere = array ('1 = 1' );
			$aVaule = null;
		}
		$sSql = 'SELECT COUNT(*) FROM `' . $sTable . '` WHERE ' . implode ( ' AND ', $aWhere );
		return $this->getOne ( $sSql, $aVaule, $sServer );
	}
	
	public function get($sTable, $iId, $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		if (is_array ( $iId )) {
			foreach ( $iId as $sKey => $sRow ) {
				if ($this->_aServerInfo [$sServer] ['type'] == 'oracle') {
					$sKey = strtoupper ( $sKey );
				}
				if (in_array ( $sFieldPre . $sKey, $aField )) {
					if (is_array ( $sRow )) {
						$aWhere [] = $sFieldPre . $sKey . ' IN (?' . str_repeat ( ',?', count ( $sRow ) - 1 ) . ')';
						$aVaule = array_merge ( $aVaule, $sRow );
					} else {
						$sValue = trim ( $sRow );
						if (preg_match ( '/^(<>|>|<|like)(.+?)$/i', $sValue, $aMatch )) {
							$aWhere [] = $sFieldPre . $sKey . $aMatch [1] . ' ?';
							$aVaule [] = trim ( $aMatch [2] );
						} else {
							$aWhere [] = $sFieldPre . $sKey . ' = ?';
							$aVaule [] = $sRow;
						}
					}
				
				}
			}
		} else {
			$aWhere [] = $sFieldPre . 'id = ?';
			$aVaule = $iId;
		}
		$sSql = 'SELECT * FROM `' . $sTable . '` WHERE ' . implode ( ' AND ', $aWhere );
		$aData = $this->getOne ( $sSql, $aVaule, $sServer );
		if (is_array ( $aData )) {
			foreach ( $aData as $sKey => $sRow ) {
				unset ( $aData [$sKey] );
				$sKey = preg_replace ( '/^' . $sFieldPre . '/', '', $sKey );
				$aData [$sKey] = $sRow;
			}
		}
		return $aData;
	}
	
	public function del($sTable, $iId, $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		if (is_array ( $iId )) {
			foreach ( $iId as $sKey => $sRow ) {
				if (in_array ( $sFieldPre . $sKey, $aField )) {
					$aWhere [] = $sFieldPre . $sKey . ' = ?';
					$aVaule [] = $sRow;
				}
			}
		} else {
			$aWhere [] = $sFieldPre . 'id = ?';
			$aVaule = $iId;
		}
		$sSql = 'DELETE FROM `' . $sTable . '` WHERE ' . implode ( ' AND ', $aWhere );
		return $this->exec ( $sSql, $aVaule, $sServer );
	}
	
	public function getArray($sTable, $sKey = 'id', $sName = 'name', $aWhere = array(), $aSort = array(), $sServer = 'db') {
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		$sSql = 'SELECT ' . $sFieldPre . $sKey . ' id,' . $sFieldPre . $sName . ' name FROM `' . $sTable . '`';
		$sGroup = ' GROUP BY ' . $sFieldPre . $sKey;
		$aSqlWhere = array ();
		$aVaule = array ();
		$aArray = array ();
		if (is_array ( $aWhere )) {
			foreach ( $aWhere as $sKey => $sRow ) {
				if (in_array ( $sFieldPre . $sKey, $aField )) {
					$sValue = trim ( $sRow );
					if (preg_match ( '/^(<>|>|<|like)(.+?)$/i', $sValue, $aMatch )) {
						$aSqlWhere [] = $sFieldPre . $sKey . $aMatch [1] . ' ?';
						$aVaule [] = trim ( $aMatch [2] );
					} else {
						$aSqlWhere [] = $sFieldPre . $sKey . ' = ?';
						$aVaule [] = $sRow;
					}
				}
			}
			if (! empty ( $aSqlWhere )) {
				$sSql .= ' WHERE ' . implode ( ' AND ', $aSqlWhere );
			}
		}
		$sSql .= $sGroup;
		if (! empty ( $aSort )) {
			$aSqlSort = array ();
			foreach ( $aSort as $sRow ) {
				list ( $sSortField, $sSortType ) = explode ( ':', $sRow );
				if ($sSortType) {
					$aSqlSort [] = $sFieldPre . $sSortField . ' ' . $sSortType;
				} else {
					$aSqlSort [] = $sFieldPre . $sSortField;
				}
			}
			$sSql .= ' ORDER BY ' . implode ( ',', $aSqlSort );
		} elseif (in_array ( $sFieldPre . 'sort', $aField )) {
			$sSql .= ' ORDER BY ' . $sFieldPre . 'sort';
		} else {
			$sSql .= ' ORDER BY ' . $sFieldPre . $sName;
		}
		$aData = $this->query ( $sSql, $aVaule );
		while ( ($aRow = Base::$oBase->Db_getRow ()) !== false ) {
			$aArray [$aRow ['id']] = $aRow ['name'];
		}
		return $aArray;
	}
	
	public function _ls($aParam) {
		$aNewParam ['where'] = array ();
		$aNewParam ['group'] = null;
		$aNewParam ['sort'] = null;
		$aNewParam ['start'] = 0;
		$aNewParam ['limit'] = 0;
		$aNewParam ['alias'] = array ();
		$aNewParam ['exception'] = array ();
		$aNewParam ['idkey'] = false;
		$aNewParam ['server'] = 'db';
		foreach ( $aParam as $sKey => $sParam ) {
			$aNewParam [$sKey] = $sParam;
		}
		return $aNewParam;
	}
	
	public function ls($sTable, $aWhere = array(), $sGroup = null, $aSort = null, $iStart = 0, $iLimit = 0, $aAlias = array(), $aException = array(), $sServer = 'db') {
		if (is_array ( $sTable )) {
			$aParam = $this->_ls ( $sTable );
			$sTable = $aParam ['table'];
			$aWhere = $aParam ['where'];
			$sGroup = $aParam ['group'];
			$aSort = $aParam ['sort'];
			$iStart = $aParam ['start'];
			$iLimit = $aParam ['limit'];
			$aAlias = $aParam ['alias'];
			$aException = $aParam ['exception'];
			$bIdKey = $aParam ['idkey'];
			$sServer = $aParam ['server'];
		}
		$aField = $this->getField ( $sTable, $sServer );
		$sFieldPre = $this->getPre ( $sTable, $sServer );
		$sSql = 'SELECT * FROM `' . $sTable . '`';
		if ($sGroup) {
			$sGroup = ' GROUP BY ' . $sGroup;
		}
		$aSqlWhere = array ();
		if (is_array ( $aWhere )) {
			foreach ( $aWhere as $sKey => $sRow ) {
				if (in_array ( $sFieldPre . $sKey, $aField )) {
					if (! is_array ( $sRow )) {
						$sValue = trim ( $sRow );
					}
					if (preg_match ( '/^(<>|>=|<=|>|<)(.+?)$/', $sValue, $aMatch )) {
						$aSqlWhere [] = $sFieldPre . $sKey . $aMatch [1] . ' ?';
						$aVaule [] = trim ( $aMatch [2] );
					} elseif (substr ( $sValue, 0, 1 ) == '%' || substr ( $sValue, - 1 ) == '%') {
						$aSqlWhere [] = $sFieldPre . $sKey . ' LIKE ?';
						$aVaule [] = $sValue;
					} elseif (is_array ( $sRow )) {
						$aSqlWhere [] = $sFieldPre . $sKey . ' IN (?' . str_repeat ( ', ?', count ( $sRow ) - 1 ) . ')';
						$aVaule = array_merge ( $sRow );
					} else {
						$aSqlWhere [] = $sFieldPre . $sKey . ' = ?';
						$aVaule [] = $sRow;
					}
				} elseif (strtolower ( $sKey ) == 'where') {
					$aSqlWhere [] = $sRow;
				}
			}
			if (! empty ( $aSqlWhere )) {
				$sSql .= ' WHERE ' . implode ( ' AND ', $aSqlWhere );
			}
		}
		if ($sGroup) {
			$sGroup = ' GROUP BY ' . $sGroup;
		}
		if (! empty ( $aSort )) {
			$aSqlSort = array ();
			foreach ( $aSort as $sRow ) {
				list ( $sSortField, $sSortType ) = explode ( ':', $sRow );
				if ($sSortType) {
					$aSqlSort [] = $sFieldPre . $sSortField . ' ' . $sSortType;
				} else {
					$aSqlSort [] = $sFieldPre . $sSortField;
				}
			}
			$sSql .= ' ORDER BY ' . implode ( ',', $aSqlSort );
		} elseif (in_array ( $sFieldPre . 'sort', $aField )) {
			$sSql .= ' ORDER BY ' . $sFieldPre . 'sort';
		}
		$iCount = $this->_getItems ( $sSql, $aVaule, $sServer );
		if ($iLimit) {
			$sSql .= ' LIMIT ' . $iStart . ', ' . $iLimit;
		}
		var_dump($sSql);
		$this->query ( $sSql, $aVaule );
		$iSize = 0;
		while ( ($aRow = Base::$oBase->Db_getRow ()) !== false ) {
			$aOne = array ();
			foreach ( $aRow as $sKey => $sValue ) {
				$sKey = preg_replace ( '/^' . $sFieldPre . '/', '', $sKey );
				if (array_key_exists ( $sKey, $aAlias )) {
					$sKey = $aAlias [$sKey];
				}
				if (! in_array ( $sKey, $aException )) {
					$aOne [$sKey] = $sValue;
				}
			}
			$iSize ++;
			if ($bIdKey) {
				$aData ['item'] [$aOne [$bIdKey]] = $aOne;
			} else {
				$aData ['item'] [] = $aOne;
			}
		}
		$aData ['count'] = $iCount;
		$aData ['size'] = $iSize;
		return $aData;
	}
	
	public function getCurrPage($iStart, $iLimit) {
		return ceil ( $iStart / $iLimit );
	}
	
	public function getStart($iPage, $iLimit) {
		if (intval ( $iPage ) <= 0) {
			return 1;
		} else {
			return ($iPage - 1) * $iLimit;
		}
	}
	
	public function getPageInfo($iStart, $iLimit, $iCount) {
		$aInfo ['page'] = floor ( $iStart / $iLimit ) + 1;
		$aInfo ['pages'] = floor ( ($iCount - 1) / $iLimit ) + 1;
		return $aInfo;
	}
}
?>